/*
 * This code is made available under version 3 of the
 * GNU GENERAL PUBLIC LICENSE. See the file LICENSE in this
 * distribution for details.
 * 
 * Copyright 2008 Eric Bodden
 */

package infiniteloop;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.aspectj.lang.JoinPoint.StaticPart;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.SourceLocation;

/**
 * Locking aspect, keeping track of thread-local lock sets.
 * 
 * @author Eric Bodden
 */
public aspect InfiniteLoop {
	public static int state=0;
	public static HashBag jointpointCache=new HashBag();
	public static int loopThreshold=100;
	protected static class JointPointObject {

		//@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((line == null) ? 0 : line.hashCode());
			result = prime * result
					+ ((sourceName == null) ? 0 : sourceName.hashCode());
			result = prime * result
					+ ((targetClass == null) ? 0 : targetClass.hashCode());
			result = prime * result
					+ ((targetMethod == null) ? 0 : targetMethod.hashCode());
			return result;
		}
		//@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			JointPointObject other = (JointPointObject) obj;
			if (line == null) {
				if (other.line != null)
					return false;
			} else if (!line.equals(other.line))
				return false;
			if (sourceName == null) {
				if (other.sourceName != null)
					return false;
			} else if (!sourceName.equals(other.sourceName))
				return false;
			if (targetClass == null) {
				if (other.targetClass != null)
					return false;
			} else if (!targetClass.equals(other.targetClass))
				return false;
			if (targetMethod == null) {
				if (other.targetMethod != null)
					return false;
			} else if (!targetMethod.equals(other.targetMethod))
				return false;
			return true;
		}
		public JointPointObject(String sourceName, String line,
				String targetClass, String targetMethod) {
			super();
			this.sourceName = sourceName;
			this.line = line;
			this.targetClass = targetClass;
			this.targetMethod = targetMethod;
		}
		String sourceName;
		String line;
		String targetClass;
		String targetMethod;


	}
	
    pointcut traceMethods() : (execution(* *(..))&& !within(InfiniteLoop)&&!cflow(within(InfiniteLoop)));
	   
	before(): traceMethods(){
	        Signature sig = thisJoinPointStaticPart.getSignature();
	        String line =""+ thisJoinPointStaticPart.getSourceLocation().getLine();
	        String sourceName = thisJoinPointStaticPart.getSourceLocation().getWithinType().getCanonicalName();
	        Logger.getLogger("Tracing").log(
	                Level.INFO, 
	                "Call from "
	                    +  sourceName
	                    +" line " +
	                    line
	                    +" to " +sig.getDeclaringTypeName() + "." + sig.getName()
	        );
			if(isInfiniteLoop(sourceName,line,sig.getDeclaringTypeName(),sig.getName()))
			{
				
				String id = getId(thisJoinPointStaticPart);
				Class owner = thisJoinPointStaticPart.getSignature().getDeclaringType();		
				SourceLocation loc = location(thisJoinPointStaticPart);
				
				System.err.println("Infinite Loop!:"+id+" "+owner+"  location:"+loc);
				System.err.println("Would you break the loop? Y/N");
				
				//if(System.in.equals("Y"))break;
				while(true)
				{
					if(System.in.equals("Y"))break;
				}
				
				
			}
	    }

	
	ThreadLocal locksHeld = new ThreadLocal() {
		 protected synchronized Object initialValue() {
			 return new HashBag();
		 }
	};
	


	private String getId(StaticPart sp) {
		return sp.getSignature().toLongString().intern();
	}
	private SourceLocation location(StaticPart sp){
		return sp.getSourceLocation();
	}
	
	public boolean isInfiniteLoop(String sourceName,String line, String targetClass, String targetMethod)
	{
		JointPointObject jpo=new JointPointObject(sourceName,line,targetClass,targetMethod);
		if(jointpointCache.contains(jpo))
		{
			
			state++;
			if(state>loopThreshold)return true;
			else return false;
		}
		else
		{
			state=0;
			jointpointCache.clear();
			jointpointCache.add(jpo);
			return false;
		}
		
	}
}
